/* Michael Lowe, Hamed Mirlohi, Tinghua Xu
 * 05/19/2012
 * ECE 373
 *
 * Final Project: Labjack Driver
 */

#include <linux/module.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/usb.h>
#include <linux/slab.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/time.h>
#include <linux/mutex.h>
#include <linux/timer.h>
#include <linux/moduleparam.h>

#define DEVNAME "Lab_Jack"
#define MY_USB_MINOR_BASE 0x0
#define LABJACK_VENDOR_ID 0x0cd5
#define LABJACK_HV_PRODUCT_ID 0x0003
#define DEVCOUNT	3	
#define PORT_A		0
#define PORT_B		1
#define PORT_C		2
#define SPAN		244

static int access_count = 0;

module_param(access_count,int, S_IRUGO);

static int labjack_probe(struct usb_interface *, const struct usb_device_id *);
static void labjack_disconnect(struct usb_interface *intf);
static int port_a_open(struct inode *inode, struct file *file);
ssize_t port_a_read(struct file *file, char __user *user_buf, size_t count, loff_t * offset);
static int port_c_open(struct inode *inode, struct file *file);
ssize_t port_c_read(struct file *file, char __user *user_buf, size_t count, loff_t * offset);

//static int port_b_open(struct inode *inode, struct file *file);
/* Port B system calls */
static int port_b_open(struct inode *inode, struct file *file);
static int port_b_monitoring( void * data);
static ssize_t port_b_read(struct file *file, char *buf, size_t count,
			   loff_t *offset);
static ssize_t port_b_write(struct file *file, const char *buffer, size_t count,
			    loff_t *offset);
static int port_b_close(struct inode *inode, struct file *file);


static void labjack_usb_delete(struct kref *kref);

void extendedChecksum( u8 *b, int n);
u16 extendedChecksum16( u8 *b, int n);
u8 extendedChecksum8( u8 *b);

struct port_args {
	struct file *file;
	unsigned char * buf;
	int	w_len;     /* Write buffer length */
	int	r_len;     /* Read buffer lengther */
};

/* device ID table */
static struct usb_device_id labjack_usb_table[] = {
	{ USB_DEVICE(LABJACK_VENDOR_ID, LABJACK_HV_PRODUCT_ID) },
	{ } 
};
MODULE_DEVICE_TABLE(usb, labjack_usb_table);


static u32 labjack_num = 0;

static struct usb_driver labjack_usb_driver = {
	.name = DEVNAME,
	.id_table = labjack_usb_table,
	.probe	= labjack_probe,
	.disconnect = labjack_disconnect,
};

static struct file_operations port_a_fops = {
	.owner = THIS_MODULE,
	.open  = port_a_open,
	.read  = port_a_read,
};


static struct file_operations port_b_fops = {
	.owner   = THIS_MODULE,
	.read    = port_b_read,
	.write   = port_b_write,
	.open    = port_b_open,
	.release = port_b_close,
};
static struct file_operations port_c_fops = {
	.owner = THIS_MODULE,
	.open  = port_c_open,
	.read  = port_c_read,
};

struct labjack_dev {
	struct usb_device	*udev;
	struct usb_interface	*interface;
	__u8			bulk_in_endpointAddr;
	__u8			bulk_out_endpointAddr;
	size_t			bulk_in_length;
	size_t			bulk_in_filled;
	size_t			bulk_in_copied;
	struct completion	bulk_in_completion;
	int			errors;
	unsigned char		*bulk_in_buf;
	struct urb		*bulk_in_urb;
	struct kref		kref;
	bool			ongoing_read;
	bool			processed_urb;
	struct cdev 		portA;
	struct cdev		portB;
	struct cdev		portC;
	dev_t 			devNode;
	struct mutex 		io_mutex;
	struct task_struct*	monitor_task;
	struct task_struct* 	monitor_temp;
	struct task_struct* 	monitor_voltage;
	int			tempK;
	bool			door_open;
	struct timer_list	portA_timer;
	unsigned long		toggle_time;
	struct port_args	*portB_args;
	struct timer_list	timer;
	bool			scrub_on;
	spinlock_t		lock;
	u8			cycle_time;	
};


static int labjack_usb_read_io(struct labjack_dev *my_dev, size_t count,u8 port);
static ssize_t labjack_usb_read(struct labjack_dev *my_dev, char *buf, size_t count,u8 port);
static int labjack_usb_write(struct labjack_dev *my_dev,char * sendBuffer, size_t count);

	static struct class*	devClass;

static void labjack_write_callback(struct urb *urb)
{
	struct labjack_dev *my_dev;
	unsigned char * buf;

	my_dev = urb->context;

	printk(KERN_INFO "in URB cleanup, status is: %d \n", urb->status);

	if(urb->status) {
	if(!(	urb->status == -ENOENT ||
		urb->status == -ECONNRESET ||
		urb->status == -ESHUTDOWN))
		printk(KERN_ERR "nonzero write bulk status received \n");
	}
	buf = urb->transfer_buffer;
	kfree(buf);
	
	//complete(&my_dev->bulk_in_completion);
}

static void labjack_read_callback_portA(struct urb *urb)
{
	struct labjack_dev *my_dev;
	u8 * buff;
	my_dev = urb->context;

	
	if (urb->status){
		if(!(	urb->status == -ENOENT ||
			urb->status == -ECONNRESET ||
			urb->status == -ESHUTDOWN))
			printk(KERN_ERR "nonzero write bulk status received\n");

		my_dev->errors = urb->status;
	}
	else{
	
		my_dev->bulk_in_filled = urb->actual_length;
	//	printk(KERN_INFO "
		if(my_dev->bulk_in_filled == 12)
		{
			buff = my_dev->bulk_in_buf;
			printk(KERN_INFO "LSB: %x MSB: %x \n",buff[9],buff[10]);
			
		}
		my_dev->ongoing_read = 0;
	}
}


int monitor_port_a(void *data)
{
	size_t count;
	struct labjack_dev *my_dev;	
	int * ret=NULL;
	int rv;
	u8 *sendBuffer = NULL ;
	u8 *recvBuff = NULL;
	u16 voltage;
	printk(KERN_INFO "Thread for port A\n");

	my_dev = data;
	if(!my_dev){
		printk(KERN_INFO "Can't find dev...\n");
		return -ENODEV;
		}


	sendBuffer = kzalloc(10, GFP_KERNEL);
	recvBuff = kzalloc(12,GFP_KERNEL);
	printk(KERN_INFO "Configure Buffer...\n");	

	count = 10;
	sendBuffer[0] = 0x00;
	sendBuffer[1] = 0xF8;
	sendBuffer[2] = 0x02;
	sendBuffer[3] = 0x00;
	sendBuffer[4] = 0x00;
	sendBuffer[5] = 0x00;
	sendBuffer[6] = 0xFA;
	sendBuffer[7] = 0x01;
	sendBuffer[8] = 0x0A;
	sendBuffer[9] = 0x1F;
		extendedChecksum(sendBuffer,10);


	/*sendBuffer[8] = 0x01;*/
	while(1)
	{

	voltage = 0;
/*	sendBuffer[0] = 0x00;
	sendBuffer[1] = 0xF8;
	sendBuffer[2] = 0x02;
	sendBuffer[3] = 0x00;
	sendBuffer[4] = 0x00;
	sendBuffer[5] = 0x00;
	sendBuffer[6] = 0x08;
	sendBuffer[7] = 0x09;
	sendBuffer[9] = 0x00;
		sendBuffer[8] = sendBuffer[8] ^ 0x01;
		extendedChecksum(sendBuffer,10);
		labjack_usb_write(my_dev,sendBuffer,10);*/
	//	labjack_usb_write(my_dev,sendBuffer,10);
	rv = usb_bulk_msg(my_dev->udev,
			usb_sndbulkpipe(my_dev->udev,my_dev->bulk_out_endpointAddr),
			sendBuffer,count,ret,10*HZ);
		//wait_for_completion(&my_dev->bulk_in_completion);
	//	labjack_usb_read(my_dev,1,12,PORT_A);
			printk(KERN_INFO "Write Ret: %x \n",rv);
	count = 12;
	rv =usb_bulk_msg(my_dev->udev,
			usb_rcvbulkpipe(my_dev->udev,my_dev->bulk_in_endpointAddr),
			recvBuff,min(my_dev->bulk_in_length,count),ret,10*HZ);
//		printk(KERN_INFO "This is thread %d,kref -> %d \n",count,MINOR(my_dev->devNode));

			printk(KERN_INFO "READ ret: %x LSB: %x MSB: %x \n",rv,recvBuff[9],recvBuff[10]);
		voltage = recvBuff[10];
		voltage = (voltage<<8) | recvBuff[9];
		voltage = voltage * SPAN / 65536;
			printk(KERN_INFO "Voltage : %x \n",voltage);

		/*if the volage gets greater than 1 volt (100) set door open to true*/
		//mutex_lock(&my_dev->io_mutex);
		if(voltage >= 100) my_dev->door_open = true;
		else my_dev->door_open = false;
		//mutex_unlock(&my_dev->io_mutex);

		count++;
		msleep(1000);
		if(kthread_should_stop()) break;	
	}
	
//	mod_timer(&my_dev->portA_timer,jiffies + msecs_to_jiffies(1000));
	return 0;

}

int monitor_port_c(void *data)
{
	size_t count;
	struct labjack_dev *my_dev;	
	int * ret=NULL;
	int rv;
	u8 *sendBuffer = NULL ;
	u8 *recvBuff = NULL;
	u16 temperature;
	printk(KERN_INFO "Thread for port C\n");

	my_dev = data;
	if(!my_dev){
		printk(KERN_INFO "Can't find dev...\n");
		return -ENODEV;
		}


	sendBuffer = kzalloc(10, GFP_KERNEL);
	recvBuff = kzalloc(12,GFP_KERNEL);
	printk(KERN_INFO "Configure Buffer...\n");	

	count = 10;
	sendBuffer[0] = 0x00;
	sendBuffer[1] = 0xF8;
	sendBuffer[2] = 0x02;
	sendBuffer[3] = 0x00;
	sendBuffer[4] = 0x00;
	sendBuffer[5] = 0x00;
	sendBuffer[6] = 0xFA;
	sendBuffer[7] = 0x01;
	sendBuffer[8] = 0x1E;
	sendBuffer[9] = 0x1F;
		
	printk(KERN_INFO "Getting Checksum...\n");	
	extendedChecksum(sendBuffer,10);


	/*sendBuffer[8] = 0x01;*/
	while(1)
	{
	
	printk(KERN_INFO "Entering Loop...\n");	
	temperature = 0;
	

	printk(KERN_INFO "Sending URB...\n");	
	rv = usb_bulk_msg(my_dev->udev,
			usb_sndbulkpipe(my_dev->udev,my_dev->bulk_out_endpointAddr),
			sendBuffer,count,ret,10*HZ);
			printk(KERN_INFO "Write Ret: %x \n",rv);
	count = 12;
	rv =usb_bulk_msg(my_dev->udev,
			usb_rcvbulkpipe(my_dev->udev,my_dev->bulk_in_endpointAddr),
			recvBuff,min(my_dev->bulk_in_length,count),ret,10*HZ);

			printk(KERN_INFO "READ ret: %x LSB: %x MSB: %x \n",rv,recvBuff[9],recvBuff[10]);
		temperature = recvBuff[10];
		temperature = (temperature<<8) | recvBuff[9];
		my_dev->tempK = (temperature * 13021)/1000000;
			printk(KERN_INFO "Temperature : %d \n",temperature);

		msleep(1000);
		if(kthread_should_stop()) break;	
	}
	

	return 0;

}



ssize_t port_a_read(struct file *file, char __user *user_buf, 
			size_t count, loff_t * offset)
{
	char *buf = "Airlock open!\n";
//	ssize_t length;
	struct labjack_dev *my_dev;	

//	length = min_t(ssize_t, count, sizeof(buf));
	my_dev = file->private_data;
	
	printk(KERN_INFO "In port A read... \n");
	
	while(1)
	{
		//mutex_lock(&my_dev->io_mutex);
		if(my_dev->door_open == true) break;
		//mutex_unlock(&my_dev->io_mutex);
		msleep(1000);
	
	}

	printk(KERN_INFO "MESSAGE BUFF: %s \n",buf);
	
	copy_to_user(user_buf, buf, 15);
	
	spin_lock(&my_dev->lock);	
	access_count++;	
	spin_unlock(&my_dev->lock);
	
	return sizeof(buf);
}


ssize_t port_c_read(struct file *file, char __user *user_buf, 
			size_t count, loff_t * offset)
{
	
	ssize_t length;
	struct labjack_dev *my_dev;	

	length = min_t(ssize_t, count, sizeof(&my_dev->tempK));
	my_dev = file->private_data;
	
	printk(KERN_INFO "In port C read... \n");
	
	copy_to_user(user_buf, &my_dev->tempK, length);
	spin_lock(&my_dev->lock);	
	access_count++;	
	spin_unlock(&my_dev->lock);
	return sizeof(&my_dev->tempK);
}

static int port_c_open(struct inode *inode, struct file *file)
{	

	struct labjack_dev *my_dev;	
//	int * ret=NULL;
//	int rv;
//	u8 *sendBuffer = NULL;

	printk(KERN_INFO "Opening port C\n");
	printk(KERN_INFO "inode addr: %p, iminor: %d\n",inode,iminor(inode));
	my_dev = container_of(inode->i_cdev,struct labjack_dev, portC);
	if(!my_dev){
		printk(KERN_INFO "Can't find dev...\n");
		return -ENODEV;
		}
	
/*	sendBuffer = kzalloc(12, GFP_KERNEL);
	printk(KERN_INFO "Configure Buffer...\n");	

	sendBuffer[1] = 0xF8;
	sendBuffer[2] = 0x03;
	sendBuffer[3] = 0x0B;
	sendBuffer[6] = 0x08;
	sendBuffer[7] = 0x00;
	sendBuffer[8] = 0x00;
	sendBuffer[9] = 0x00;
	sendBuffer[10]= 0x00;
	sendBuffer[11]= 0x04;
	extendedChecksum(sendBuffer,12);

	
	//labjack_usb_write(my_dev, sendBuffer,12);
	rv = usb_bulk_msg(my_dev->udev,
			usb_sndbulkpipe(my_dev->udev,my_dev->bulk_out_endpointAddr),
			sendBuffer,12,ret,10*HZ);
*/	
	my_dev->monitor_temp = kthread_run(monitor_port_c,my_dev,"portC_thread");		
	//init_timer(&my_dev->portA_timer);
//	add_timer(&my_dev->portA_timer);

//	mod_timer(&my_dev->portA_timer,jiffies + msecs_to_jiffies(100));
	kref_get(&my_dev->kref);
	
	file->private_data = my_dev;
	
	printk(KERN_INFO "Finishing Port C open \n");

	return 0;
}


static int port_a_open(struct inode *inode, struct file *file)
{	

	struct labjack_dev *my_dev;	
	int * ret=NULL;
	int rv;
	u8 *sendBuffer = NULL;

	printk(KERN_INFO "Opening port A\n");
	printk(KERN_INFO "inode addr: %p, iminor: %d\n",inode,iminor(inode));
	my_dev = container_of(inode->i_cdev,struct labjack_dev, portA);
	if(!my_dev){
		printk(KERN_INFO "Can't find dev...\n");
		return -ENODEV;
		}
	
	sendBuffer = kzalloc(12, GFP_KERNEL);
	printk(KERN_INFO "Configure Buffer...\n");	

	sendBuffer[1] = 0xF8;
	sendBuffer[2] = 0x03;
	sendBuffer[3] = 0x0B;
	sendBuffer[6] = 0x08;
	sendBuffer[7] = 0x00;
	sendBuffer[8] = 0x00;
	sendBuffer[9] = 0x00;
	sendBuffer[10]= 0x00;
	sendBuffer[11]= 0x04;
	extendedChecksum(sendBuffer,12);

	
	//labjack_usb_write(my_dev, sendBuffer,12);
	rv = usb_bulk_msg(my_dev->udev,
			usb_sndbulkpipe(my_dev->udev,my_dev->bulk_out_endpointAddr),
			sendBuffer,12,ret,10*HZ);
	
	my_dev->monitor_task = kthread_run(monitor_port_a,my_dev,"portA_thread");		
	//init_timer(&my_dev->portA_timer);
//	add_timer(&my_dev->portA_timer);

//	mod_timer(&my_dev->portA_timer,jiffies + msecs_to_jiffies(100));
	kref_get(&my_dev->kref);
	
	file->private_data = my_dev;
	
	printk(KERN_INFO "Finishing Port A open \n");

	return 0;
}



/*static int port_b_open(struct inode *inode, struct file *file)
{

	struct labjack_dev *my_dev;	
	int * ret=NULL;
	int rv;

	u8 *sendBuffer = NULL;
	printk(KERN_INFO "Opening port B\n");
	printk(KERN_INFO "inode addr: %p, iminor: %d\n",inode,iminor(inode));
	my_dev = container_of(inode->i_cdev,struct labjack_dev, portB);
	if(!my_dev)
		{
		printk(KERN_INFO "Can't find dev...\n");
		return -ENODEV;
		}

sendBuffer = kzalloc(10, GFP_KERNEL);

printk(KERN_INFO "Configure Buffer...\n");	

	sendBuffer[1] = 0xF8;
	sendBuffer[2] = 0x02;
	sendBuffer[3] = 0x00;
	sendBuffer[7] = 0x0B;
	sendBuffer[8] = 0x04;		// state OFF
	sendBuffer[8] = sendBuffer[8] ^ 0x80;	// turning to ON

	extendedChecksum(sendBuffer,10);


	rv = usb_bulk_msg(my_dev->udev,

	usb_sndbulkpipe(my_dev->udev,my_dev->bulk_out_endpointAddr),

	sendBuffer,10,ret,10*HZ);
return 0;

	

}
*/

static int port_b_open(struct inode *inode, struct file *file)
{
	unsigned char *buf;
	int writesize = 10, written;
	int readsize = 12;
	struct labjack_dev *my_dev;
	int ret = 0;
	int * rv=NULL;
	

	printk(KERN_INFO "Opening port_b!\n");

	my_dev = container_of(inode->i_cdev, struct labjack_dev, portB);
	if (!my_dev) {
		printk(KERN_INFO "Can't find device, again...\n");
		ret = -ENODEV;
		goto exit;
	}

	/* increment refcnt for the device */
	kref_get(&my_dev->kref);

	file->private_data = my_dev;
	
	
	/* Command to toggel the volts, use the command BitStateWrite IOtype 11*/
	buf = kzalloc(writesize, GFP_KERNEL);
	buf[1] = 0xF8;
	buf[2] = 0x02;
	buf[3] = 0x00;
	buf[6] = 0x05;
	buf[7] = 0x0b;  /* IOtype=11 */
	buf[8] = 0x04;      
	buf[9] = 0x00;
	extendedChecksum(buf, writesize);
	my_dev->scrub_on = true;
	
	written = usb_bulk_msg(my_dev->udev,
			usb_sndbulkpipe(my_dev->udev,my_dev->bulk_out_endpointAddr),
			buf,writesize,rv,10*HZ);
	printk(KERN_INFO "%d bytes written to the device\n", written);
	my_dev->toggle_time = jiffies;
	
	my_dev->portB_args = kzalloc( sizeof(struct port_args), GFP_KERNEL);
	my_dev->portB_args->file = file;
	my_dev->portB_args->buf = buf;
	my_dev->portB_args->w_len = writesize;
	my_dev->portB_args->r_len = readsize;

	my_dev->monitor_voltage = kthread_run(port_b_monitoring,my_dev,"portB_thread");		
/*	setup_timer(&my_dev->timer, port_b_monitoring, (unsigned long) my_dev->portB_args );
	if ( my_dev->cycle_time )  
		mod_timer( &my_dev->timer, jiffies + msecs_to_jiffies(my_dev->cycle_time * 1000) );
	else  
		mod_timer( &my_dev->timer, jiffies + msecs_to_jiffies(5000) );
*/	
exit:
	return ret;

}
int port_b_monitoring( void *data)
{
	struct labjack_dev *my_dev;
	unsigned char *wbuf;
	int writesize,readsize,written;
	struct port_args *args;
//	struct file *file;
	int * ret=NULL;
	
	my_dev = data;

	args = (struct port_args *) data;
//	file = my_dev->portB_args->file;
	wbuf = my_dev->portB_args->buf;
	writesize = my_dev->portB_args->w_len;
	readsize = my_dev->portB_args->r_len;
//	my_dev = file->private_data;
	
	printk(KERN_INFO "Inside PortB Monitoring!\n");
	
	while(1)
	{	
		if(my_dev->scrub_on == true)		
		{
			wbuf[8] = 0x04;
			my_dev->scrub_on = false;
		}
		else
		{
			wbuf[8] = 0x84;
			my_dev->scrub_on = true;
		}      
		
		extendedChecksum(wbuf, writesize);

		written = usb_bulk_msg(my_dev->udev,
				usb_sndbulkpipe(my_dev->udev,my_dev->bulk_out_endpointAddr),
				wbuf,writesize,ret,10*HZ);
		printk(KERN_INFO "%d bytes written to the device\n", written);
		my_dev->toggle_time = jiffies_to_msecs(jiffies);
				
	if ( my_dev->cycle_time ) { 
		msleep(my_dev->cycle_time * 1000);
		printk(KERN_INFO "port_b toggels every %d seconds!\n", my_dev->cycle_time);
	 } else  {
		msleep(5000);
		printk(KERN_INFO "port_b toggels every %d seconds!\n", 60);
		}

		if(kthread_should_stop()) break;	
	}
/*	if ( my_dev->cycle_time ) { 
		mod_timer( &my_dev->timer, jiffies + msecs_to_jiffies(my_dev->cycle_time * 1000) );
		printk(KERN_INFO "port_b toggels every %d seconds!\n", my_dev->cycle_time);
	 } else  {
		mod_timer( &my_dev->timer, jiffies + msecs_to_jiffies(5000) );
		printk(KERN_INFO "port_b toggels every %d seconds!\n", 60);
	}*/
	return 0;

}

static ssize_t port_b_read(struct file *file, char *buf, size_t count,
			   loff_t *offset)
{
	unsigned char secs;
	struct labjack_dev *my_dev;
	

	printk(KERN_INFO "Reading from port_b!\n");
	my_dev = file->private_data;

	/* Get the seconds passed since the last toggle and copy it to user */
	secs = (jiffies_to_msecs(jiffies)- my_dev->toggle_time)/1000 ;
	printk(KERN_INFO "The seconds passed since the last toggle is %u!\n", secs);
	copy_to_user(buf, &secs, 1);
    	
	spin_lock(&my_dev->lock);	
	access_count++;	
	spin_unlock(&my_dev->lock);
	return 1;
}

static ssize_t port_b_write(struct file *file, const char *buffer, size_t count,
			    loff_t *offset)
{
	struct labjack_dev *my_dev;

	my_dev = file->private_data;

	if (count == 0)
		return 0;

	if (copy_from_user(&my_dev->cycle_time, buffer, 1)) 
		return -EFAULT;
	spin_lock(&my_dev->lock);	
	access_count++;	
	spin_unlock(&my_dev->lock);

	return 1;
}

static int port_b_close(struct inode *inode, struct file *file)
{
	unsigned char *buf;
	int writesize = 10, written;
	struct labjack_dev *my_dev;
	int * ret=NULL;


	printk(KERN_INFO "Closing port_b!\n");
	my_dev = file->private_data;
	if (my_dev == NULL)
		return -ENODEV;
	
	/* Set the voltage as 0 */
	buf = kzalloc(writesize, GFP_KERNEL);
	buf[1] = 0xF8;
	buf[2] = 0x02;
	buf[3] = 0x00;
	buf[6] = 0x04;
	buf[7] = 0x0b;	    /* BitStateWrite 11 */
	buf[8] = 0x04;      /*FIO4 is 4 or 0x04, bit 7 is the state*/
	buf[9] = 0x00;      
	extendedChecksum(buf, 10);
	written = usb_bulk_msg(my_dev->udev,
			usb_sndbulkpipe(my_dev->udev,my_dev->bulk_out_endpointAddr),
			buf,writesize,ret,10*HZ);
	printk(KERN_INFO "%d bytes written to the device\n", written);
	kfree(buf);

	del_timer( &my_dev->timer );
	kfree(my_dev->portB_args->buf);
	kfree(my_dev->portB_args);

	 /* drop our reference to the object, need to double check as my_usb_delete frees my_dev */
    	kref_put(&my_dev->kref, labjack_usb_delete);
	printk(KERN_INFO "port_b closed!\n");

	return 0;
	
}


static int labjack_probe(struct usb_interface *intf, const struct usb_device_id *id)
{

	struct usb_host_interface *iface_desc;
	struct usb_endpoint_descriptor *endpoint;

	struct labjack_dev *my_dev;
	int i;
		
	
	printk(KERN_INFO "made it into probe\n");

	my_dev = kzalloc(sizeof(struct labjack_dev),GFP_KERNEL);


	my_dev->udev = usb_get_dev(interface_to_usbdev(intf));
	my_dev->interface = intf;

	iface_desc = intf->cur_altsetting;

	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
		endpoint = &iface_desc->endpoint[i].desc;

		if (!my_dev->bulk_in_endpointAddr && 
			usb_endpoint_is_bulk_in(endpoint))
		{
			 printk(KERN_INFO "FOUND IN\n");
			my_dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
			my_dev->bulk_in_length = le16_to_cpu(endpoint->wMaxPacketSize);
			my_dev->bulk_in_buf = kmalloc(my_dev->bulk_in_length,GFP_KERNEL);
			my_dev->bulk_in_urb = kmalloc(my_dev->bulk_in_length, GFP_KERNEL);
			if(!my_dev->bulk_in_urb)
			{	
				printk("ERROR allocating urb...\n");
				return -ENOMEM;
			}

		}
		if(!my_dev->bulk_out_endpointAddr && usb_endpoint_is_bulk_out(endpoint))
		{
			 printk(KERN_INFO "FOUND OUT\n");
			my_dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
		}
	}

	  

	usb_set_intfdata(intf,my_dev);
	kref_init(&my_dev->kref);

	/*Dynamic allocation of major device number*/
	if (alloc_chrdev_region(&my_dev->devNode, 0, DEVCOUNT,DEVNAME) <0)
	{
		printk(KERN_DEBUG "Failed to register device \n");
		return -1;
	}

	printk(KERN_INFO "Major Number: %d, Minor Number: %d\n",MAJOR(my_dev->devNode), MINOR(my_dev->devNode));

	cdev_init(&my_dev->portA,&port_a_fops);
	cdev_init(&my_dev->portB,&port_b_fops);
	cdev_init(&my_dev->portC,&port_c_fops);

	cdev_add(&my_dev->portA, my_dev->devNode, 1);
	cdev_add(&my_dev->portB, my_dev->devNode+1,1);
	cdev_add(&my_dev->portC, my_dev->devNode+2,1);

	spin_lock_init(&my_dev->lock);

	labjack_num++;
	
	device_create(devClass,&intf->dev,my_dev->devNode,&labjack_usb_driver,"labjack%dportA",labjack_num);
	device_create(devClass,&intf->dev,my_dev->devNode+1,&labjack_usb_driver,"labjack%dportB",labjack_num);
	device_create(devClass,&intf->dev,my_dev->devNode+2,&labjack_usb_driver,"labjack%dportC",labjack_num);

		

	return 0;
}


static int labjack_usb_write(struct labjack_dev *my_dev, char *sendBuffer, size_t count)
{

	struct urb *urb = NULL;
	int ret;
	
	printk(KERN_INFO "Fill URB...\n");

	urb = usb_alloc_urb(0, GFP_KERNEL);
	if(!urb) {
		printk(KERN_INFO "urb error \n");
		return -ENOMEM;
		}
	
	/*fill urb*/

	usb_fill_bulk_urb(urb,my_dev->udev,
			usb_sndbulkpipe(my_dev->udev,my_dev->bulk_out_endpointAddr),
			sendBuffer,count,labjack_write_callback,my_dev);
	ret = usb_submit_urb(urb,GFP_KERNEL);
	msleep(10);

	printk(KERN_INFO "USB Submission returns %d\n", ret);

	usb_free_urb(urb);

	return 0;
}


static ssize_t labjack_usb_read(struct labjack_dev *my_dev, char *buf, size_t count,
			   u8 port)
{
	
	int rv;

	printk(KERN_INFO "passing port: %d \n", port);

	if (!my_dev->bulk_in_urb || !count)
		return 0;

	if (!my_dev->processed_urb) {
		my_dev->bulk_in_copied = 0;
		my_dev->processed_urb = 1;
	}

        /* errors must be reported */
	rv = my_dev->errors;
	if (rv < 0) {
		/* any error is reported once */
		my_dev->errors = 0;
		/* to preserve notifications about reset */
		rv = (rv == -EPIPE) ? rv : -EIO;
		/* no data to deliver */
		my_dev->bulk_in_filled = 0;
		/* report it */
		goto exit;
	}

	/*
	 * if the buffer is filled we may satisfy the read
	 * else we need to start IO
	 */

	if (my_dev->bulk_in_filled) {
		/* we had read data */
		size_t available = my_dev->bulk_in_filled -
		                   my_dev->bulk_in_copied;
		size_t chunk = min(available, count);

		if (!available) {
			/*
			 * all data has been used
			 * actual IO needs to be done
			 */
			rv = labjack_usb_read_io(my_dev, count,port);
			if (rv < 0)
				goto exit;
		}
		/*
		 * data is available
		 * chunk tells us how much shall be copied
		 */

		/*if (copy_to_user(buf,
				my_dev->bulk_in_buf + my_dev->bulk_in_copied,
				chunk))
			rv = -EFAULT;
		else*/
			rv = chunk;

		my_dev->bulk_in_copied += chunk;
		if (available < count)
			labjack_usb_read_io(my_dev, count - chunk,port);
	} else {
		/* no data in the buffer */
		rv = labjack_usb_read_io(my_dev, count,port);
		if (rv < 0)
			goto exit;
		rv = -EAGAIN;
	}
exit:
        return rv;
}


static int labjack_usb_read_io(struct labjack_dev *my_dev, size_t count,u8 port)
{
	int ret;

	/*prepare read*/
if(port == PORT_A){
	usb_fill_bulk_urb(my_dev->bulk_in_urb,
			my_dev->udev,
	usb_rcvbulkpipe(my_dev->udev,my_dev->bulk_in_endpointAddr),
	my_dev->bulk_in_buf,
	min(my_dev->bulk_in_length,count),
	labjack_read_callback_portA,my_dev);
}
	printk(KERN_INFO "RESPONSE : %x \n",*my_dev->bulk_in_buf);

	ret = usb_submit_urb(my_dev->bulk_in_urb, GFP_KERNEL);
	if(ret <0)	{
		printk(KERN_ERR "Failed submitting read urb...\n");
		my_dev->bulk_in_filled = 0;
		ret = (ret == -ENOMEM) ? ret : -EIO;
	}
	
	return ret;


}


static void labjack_disconnect(struct usb_interface *intf)
{
	struct labjack_dev *my_dev;

	my_dev = usb_get_intfdata(intf);

	usb_set_intfdata(intf,NULL);
	
	printk(KERN_INFO "Disconnecting\n");	

	/*stop thread*/
	if(my_dev->monitor_task != NULL) kthread_stop(my_dev->monitor_task);

	if(my_dev->monitor_temp != NULL) kthread_stop(my_dev->monitor_temp);
	if(my_dev->monitor_voltage != NULL) kthread_stop(my_dev->monitor_voltage);
	/*Destroy char devs*/
	device_destroy(devClass,my_dev->devNode);
	device_destroy(devClass,my_dev->devNode + 1);	
	device_destroy(devClass,my_dev->devNode + 2);
	
	cdev_del(&my_dev->portA);
	cdev_del(&my_dev->portB);
	cdev_del(&my_dev->portC);
	unregister_chrdev_region(my_dev->devNode, 1);
	unregister_chrdev_region(my_dev->devNode + 1, 1);
	unregister_chrdev_region(my_dev->devNode + 2, 1);
	printk(KERN_INFO "disconnecting labjack\n");
}

static void labjack_usb_delete(struct kref *kref)
{
	struct labjack_dev *my_dev = container_of(kref, struct labjack_dev, kref);

	usb_free_urb(my_dev->bulk_in_urb);
	usb_put_dev(my_dev->udev);
	kfree(my_dev->bulk_in_buf);
	kfree(my_dev);
}

u16 extendedChecksum16(u8 *b, int n)
{
    int i, a = 0;

    //Sums bytes 6 to n-1 to a unsigned 2 byte value
    for( i = 6; i < n; i++ )
        a += (u16)b[i];

    return a;
}


u8 extendedChecksum8(u8 *b)
{
    int i, a, bb;

    //Sums bytes 1 to 5. Sums quotient and remainder of 256 division. Again,
    //sums quotient and remainder of 256 division.
    for( i = 1, a = 0; i < 6; i++ )
        a += (u16)b[i];

    bb=a / 256;
    a=(a - 256*bb) + bb;
    bb=a / 256;

    return (u8)((a - 256*bb) + bb);
}




void extendedChecksum(u8 *b, int n)
{
    u16 a;

    a = extendedChecksum16(b, n);
    b[4] = (u8)(a & 0xFF);
    b[5] = (u8)((a/256) & 0xFF);
    b[0] = extendedChecksum8(b);
}


/*Initialization function for labjack Driver*/
int __init labjack_init(void)
{
	printk(KERN_INFO "Lab Jack module is loading...\n");
	devClass = class_create(THIS_MODULE,DEVNAME);	
	return usb_register(&labjack_usb_driver);	
}


void __exit labjack_exit(void)
{
	usb_deregister(&labjack_usb_driver);
	class_destroy(devClass);
	printk(KERN_INFO "Lab Jack module unloading...\n");
}



MODULE_AUTHOR("Michael Lowe, Hamed Mirlohi,Tinghua Xu");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.1");
module_init(labjack_init);
module_exit(labjack_exit);
